package com.yukefms.jointpoint;

import com.yukefms.jointpoint.interfaces.Position;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;

/**
 * the point for body, wherein, there are might 25 points or 17 points, and is of 3D or 2D . Furthermore, the point data
 * is corresponding to the Earth/Camera coordination, i.e.,
 * <ul>
 *     <li> x : Horizontal line and the right hand direction is the positive direction.</li>
 *     <li> y:  Vertical line and the upper direction is its positive direction. </li>
 *     <li> Z:  Distance between body and camera. </li>
 * </ul>
 * Joint Type
 * @author Ping
 * @date 2021-6-15
 *
 */
public class BodyPointCloud {
    /**
     * Joint map, with joint type (or joint id) as key and joint with value .
     */
    private Map<String, Joint> jointMap ;
    /**
     * body id for user, which could be user id.
     */
    private String bId ;

    public BodyPointCloud(String bId) {
        this.bId = bId;
        this.jointMap = new HashMap<>() ;
    }
    public BodyPointCloud addJoint(Joint joint) {
        this.jointMap.put(joint.getJointType(), joint) ;
        return this ;
    }

    public String getbId() {
        return bId;
    }

    public void setbId(String bId) {
        this.bId = bId;
    }

    /**
     * get joints in the map by given multiple joint types.
     * @param jointTypes
     * @return
     */
    public List<Joint> getJoints(String ... jointTypes) {
        if (jointTypes == null) {
            return null ;
        }
        List<Joint> joints = new ArrayList<>() ;
        for (String jointType : jointTypes) {
            if (jointMap.containsKey(jointType)) {
                joints.add(jointMap.get(jointType)) ;
            }
        }
        return joints ;
    }

    /**
     * get position for given jointType in the bodyPointCloud.
     * @param jointType
     * @return
     */
    public Position getPosition(String jointType) {
        Joint jo = jointMap.get(jointType) ;
        return jo == null ? null : jo.getPosition() ;
    }

    /**
     * filter unrelated joints from pointMap.
     * @param unrelated
     */
    public void filter(Predicate<Joint> unrelated) {
        for (String key : this.jointMap.keySet()) {
            if (unrelated.test(this.jointMap.get(key))) {
                this.jointMap.remove(key) ;
            }

        }
    }

    /**
     * select joints by given related standard.
     * @param related
     * @return
     */
    public Map<String, Joint> select(Predicate<Joint> related) {
        Map<String, Joint> map = new HashMap<>() ;
        for (Map.Entry<String, Joint> entry : this.jointMap.entrySet()) {
           String k ;
           Joint v ;
           k = entry.getKey() ;
           v = entry.getValue() ;
           // invoke related function.
           if (related.test(v)) {
               map.put(k, v) ;
           }
        }
        return map ;
    }

    /**
     * select joints by given jointTypes
     * @param jointTypes
     * @return
     */
    public Map<String, Joint> select(List<String> jointTypes) {
        return select((joint)->jointTypes.contains(joint.getJointType())) ;
    }

    /**
     * check whether all involved joints are relevant, i.e., tracked.
     * @param jointTypes
     * @return
     */
    public boolean allRelevant(List<String> jointTypes) {
        if (jointTypes == null) {
            return false ;
        }
        for (String joint : jointTypes) {
            if (this.jointMap.get(joint) == null) {
                return false ;
            }
            if (! this.jointMap.get(joint).isTracked()) {
                return false ;
            }
        }
        return true ;

    }


    public int jointCount() {
        return this.jointMap.size() ;
    }


    @Override
    public String toString() {
        return "BodyPointCloud{" +
                "bId=" + bId  +
                jointString() +
                '}';
    }
    private String jointString() {
        StringBuilder sb = new StringBuilder() ;
        if (this.jointMap.size() > 0) {
            sb.append("[") ;
            for (String key : this.jointMap.keySet()) {
                sb.append(key).append(":") ;
                sb.append(this.jointMap.get(key)) ;
                sb.append("|") ;
            }
            sb.append("]") ;
        }
        return sb.toString() ;

    }
}
